package com.atguigu.juc.locks;

import org.junit.jupiter.api.Test;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author yanglin
 * @create 2024-01-07 17:37 星期日
 * description:  可重入锁
 */
public class ReEntryLockDemo {

    /**
     * 可重入锁：
     *  可重复可递归调用的锁，在外层使用的锁之后，在内层任然可以使用，并且不发生死锁，这样的锁叫做可重入锁
     *      synchronized 是可重入锁
     *      
     *  原理：
     *      当执行monitorenter时，如果目标锁对象的计数器为零，说明该锁对象没有被其他线程所持有，Java虚拟机会将该锁对象的
     *      持有线程设置为当前线程，并且将其计数器加 1
     *      在目标锁对象的计数器不为零的情况下，如果锁对象的持有线程是当前线程，那么Java虚拟机可将锁对象的计数器加1
     *      否则需要等待，知道持有线程释放锁
     *      当执行monitorexit时，Java虚拟机则将锁对象的计数器减1，当锁对象的计数器为0，当表该锁资源以释放
     *      
     *  注意：
     *      正常情况下，加锁几次，就要解锁几次，否则将会造成其他线程获取不到锁，导致其一直等待
     */

    @Test
    public void testReEntryLock1(){ 
        final Object o = new Object();
        
        new Thread(() -> {
            synchronized (o){
                System.out.println(Thread.currentThread().getName() + "\t  外层调用");
                synchronized (o){
                    System.out.println(Thread.currentThread().getName() + "\t  中层调用");
                    synchronized (o){
                        System.out.println(Thread.currentThread().getName() + "\t  内层调用");
                    }
                }
            }
            
        },"t1").start();
    }
    
    public synchronized void m1(){
        System.out.println(Thread.currentThread().getName() + "\t come in");
        m2();
        System.out.println(Thread.currentThread().getName() + "\t end");
    }
    
    public synchronized void m2(){
        System.out.println(Thread.currentThread().getName() + "\t come in m2");
        m3();
        System.out.println(Thread.currentThread().getName() + "\t end m2");
    }

    public synchronized void m3(){
        System.out.println(Thread.currentThread().getName() + "\t come in m3");
        System.out.println(Thread.currentThread().getName() + "\t end m3");
    }
    
    @Test
    public void testReEntryLock2(){
        ReEntryLockDemo demo = new ReEntryLockDemo();
        new Thread(() -> {
            demo.m1();
        },"t2").start();
    }
    
    //ReEntryLock：显示锁   可重入锁
    
    static Lock lock = new ReentrantLock();
    
    @Test
    public void testReEntryLock3(){ 
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "\t come in 外层调用");
                lock.lock();
                try {
                    //锁重入
                    System.out.println(Thread.currentThread().getName() + "\t come in 内层调用");
                }finally {
                    lock.unlock();
                }
            }finally {
                lock.unlock();
            }
        },"t1").start();
    }
    
    
}
